#License: GNU http://www.gnu.org/copyleft/gpl.html
#Author: Willy Ma
#Mail: gump.dev@gmail.com
import os, getopt, sys
import shutil
import errno
import ConfigParser
import getopt

TEMP_BASE = "TempChangeList\\"
BASE_COPY_PATH = "base\\"
WORK_COPY_PATH = "work\\"
CONFIG_FILE_NAME = "review.ini"
clientFileList = []
serverFileList = []
EXTENTIONS = ['.cpp', '.CPP', '.h', '.c', '.py', '.vcproj', '.ipp', '.hpp', '.sln', '.txt','.rc', '.rc2', '.bat']
FILE_STATUS = ('M', 'C', 'A', 'G', 'R') #use command 'svn help status' to see.
SERVER_SKIP_STATUS = ('A')

class dirError(Exception):
    pass

class changeListError(Exception):
    pass

def makePath(path):
    try:
        os.makedirs(path)
    except OSError, err:
        if err.errno != errno.EEXIST or not os.path.isdir(path):
            raise dirError
    return

def preparePath(No, base):
    initPath = base + "%d" % (No)
    destPath = initPath + '~1'
    count = 2
    while (os.path.exists(destPath) == True):
        destPath = initPath + '~%d' % (count)
        count = count + 1

    destPath = destPath + "\\"
    return destPath

def getChangeList(client, filter):
#call 'svn status --ignore-externals'https://orangegumpbai.googlecode.com/svn/demo/
    global clientFileList
    print "Get change list..."
    s = os.popen('svn status --ignore-externals '+ client, "r")

    lines = s.readlines()

    if len(lines) == 0: #error happened
        raise changeListError

    changeList = []
    for line in lines:
        line.rstrip('\n')
        if line != "":
            changeList.append(line.strip())

    #1. do something with filename. e.g. filter unused file type.
    #2. save the file list both for server and client 
    for item in changeList:
        if item[0] in FILE_STATUS:
            pos = item.upper().find(client.upper())
            if (pos != -1):
                tmpStr = item[pos+len(client):]
                extPos = tmpStr.rfind('.')
                if ((extPos != -1) and (tmpStr[extPos:].strip() in filter) == True):
                    clientFileList.append(tmpStr.rstrip('\n'))
                    if (item[0] not in SERVER_SKIP_STATUS): 
                        serverFileList.append(tmpStr.rstrip('\n'))
                    print tmpStr.rstrip('\n')
    if len(clientFileList)==0:
        return False
    return True

def getBaseFiles(deployPath, serverBase):
    global serverFileList
    for item in serverFileList:
        parts = os.path.split(item)
        destPath = deployPath + BASE_COPY_PATH + parts[0] + '\\'
        try:
            makePath(destPath)
        except dirError:
            raise dirError
        serverFile = item.replace('\\', '/')
        print "Get " + item + " from SVN server..."
        CMD = 'svn cat '+ '"' + serverBase + serverFile + '"' +' > ' + '"' + destPath + parts[1] + '"'
        #print CMD
        os.system(CMD)
    return

def getClientFiles(deployPath, clientBase):
    global clientFileList
    for item in clientFileList:
        parts = os.path.split(item)
        destPath =  deployPath + WORK_COPY_PATH + parts[0] + '\\'
        try:
            makePath(destPath)
        except dirError:
            raise dirError

        print "Get " + item + " from client..."
        shutil.copy(clientBase+item, destPath + parts[1]) 
    return

def callDiff(diffTool, base, work):
    print "Comparing..."
    DiffCmd = '"' +diffTool + '"' + ' ' + base + ' ' + work
    os.system(DiffCmd)
    return

def loadConfig(file):
    cp = ConfigParser.ConfigParser()
    cp.read(file)
    dic = {}
    dic['client_path'] = cp.get("CONFIG", "client_path")
    dic['server_path'] = cp.get("CONFIG", "server_path")
    dic['compare_tool'] = cp.get("CONFIG", "compare_tool")
    dic['support_extentions'] = cp.get("CONFIG", "support_extentions")
    return dic

def createConfig(file):
    s = sys.stdin
    cp = ConfigParser.RawConfigParser()
    cp.add_section("CONFIG")
    print "*Please input the client path you want to review:"
    print "(e.g. \\\\10.10.10.10\\project1\\ Notice: The last backslash is needed.)"
    client_path = s.readline().rstrip('\n')
    print "*Please input the server path where to get the base files:"
    print "(e.g. http://www.foo.com/svn/trunk/ Notice: The last slash is needed.)"
    server_path = s.readline().rstrip('\n')
    print "*Please input the path where the compare tool you want to use:"
    print "(e.g. D:\\program files\\beyond compare 2\\bc2.exe)"
    compare_tool = s.readline().rstrip('\n')

    if ((len(client_path) > 0) and (client_path[len(client_path)-1] != "\\")):
        client_path = client_path + '\\'
    if ((len(server_path) > 0) and (server_path[len(server_path)-1] != "/")):
        server_path = server_path + '/'
    cp.set("CONFIG", "client_path", client_path)
    cp.set("CONFIG", "server_path", server_path)
    cp.set("CONFIG", "compare_tool", compare_tool)

    exts=""
    for ext in EXTENTIONS:
        exts = exts + ' ' + ext
    cp.set("CONFIG", "support_extentions", exts)

    f = open(file, 'w')
    cp.write(f)
    f.close()
    print "done."
    
def changeConfig(file):
    confs = loadConfig(file)
    s = sys.stdin
    cp = ConfigParser.RawConfigParser()
    cp.add_section("CONFIG")

    for key in confs.keys():
        print "*Change the %s? Current value is [%s]. Press Enter to keep the old." % (key, confs[key])
        user_input = s.readline().rstrip('\n')
 
        if user_input != "":
            if (key=="client_path" and user_input[len(user_input)-1]!='\\'):
                user_input = user_input + '\\'
            if (key=="server_path" and user_input[len(user_input)-1]!='/'):
                user_input = user_input + '/'
            temp = user_input
        else:
            temp = confs[key]
        cp.set("CONFIG", key, temp)

    f = open(file, 'w')
    cp.write(f)
    f.close()
    print "done."
    return

def usage():
    print "Usage: %s CHANGE_NO\n-c\tedit configuation\n-v\tdiplay configuation" % (sys.argv[0])

def main():
    try:
        parts = os.path.split(sys.argv[0])
        script_path = parts[0] + "\\"
        CONFIG_FILE = script_path + CONFIG_FILE_NAME

        isConfigged = os.path.exists(CONFIG_FILE)
        opts, args = getopt.getopt(sys.argv[1:], "cv")
        for o, a in opts:
            if o == '-c':
                if isConfigged == True:
                    changeConfig(CONFIG_FILE)
                else:
                    createConfig(CONFIG_FILE)
                return
            if (o == '-v')and (isConfigged==True):
                confs = loadConfig(CONFIG_FILE)
                for key in confs.keys():
                    print key, '\t', confs[key] 
                return
       
        if isConfigged == False:
            print "**Welcome! I think you use the review script for first time.\n" + "Try to use option -c to config the script."
            return

        if len(args) != 1:
            usage()
            return
        
        confs = loadConfig(CONFIG_FILE)
        CLIENT_BASE = confs['client_path']
        SERVER_BASE = confs['server_path']
        COMPARE_TOOL = confs['compare_tool']
        EXTS = confs['support_extentions']
        
        extentions = []
        for ext in EXTS.split():
            extentions.append(ext)
        number = int(args[0])
        depotPath = preparePath(number, script_path+TEMP_BASE)
        if (getChangeList(CLIENT_BASE, extentions) == True):
            print "-----------------------------------------------------------"
            getBaseFiles(depotPath, SERVER_BASE)
            print "-----------------------------------------------------------"
            getClientFiles(depotPath, CLIENT_BASE)
            print "-----------------------------------------------------------"
            callDiff(COMPARE_TOOL, depotPath+BASE_COPY_PATH, depotPath+WORK_COPY_PATH)
        else:
            print "No file changed."
        print "done."
        
    except getopt.GetoptError:
        print usage()
    except ValueError:
        print "Parameter should be integer."
    except dirError:
        print "Create review directory error."
    except changeListError:
        print "Could not get client's change list."
    except:
        print "Unexpected error."
    return

if __name__ == '__main__':
    main()
